"""
文件名: cache.py
描述: 缓存装饰器和工具函数

本模块包含以下主要功能：
1. 函数结果缓存装饰器
2. Redis缓存键管理
3. 缓存失效和更新策略
4. 性能监控和统计

依赖模块:
   - redis: Redis客户端
   - functools: 装饰器工具
   - core: 配置和日志模块

使用示例:
   >>> @cache_result(ttl=300)
   >>> async def expensive_function():
   >>>     return calculate_something()

注意事项:
   - 缓存键要考虑参数和用户隔离
   - TTL要根据数据更新频率设置
   - 大对象缓存要考虑内存占用
   - 分布式环境要考虑缓存一致性

作者: AI助手
创建日期: 2024-06-27
最后修改: 2024-06-27
版本: 1.0.0
"""

import hashlib
import json
import pickle
from datetime import datetime, timedelta
from functools import wraps
from typing import Any, Callable, Dict, Optional, Union, List
import asyncio

from src.core.redis import get_redis_manager
from src.core.logger import get_logger
from src.core.config import get_settings

settings = get_settings()
logger = get_logger(__name__)


class CacheManager:
    """缓存管理器类"""
    
    def __init__(self):
        self.redis = None
        self.hit_count = 0
        self.miss_count = 0
        self.error_count = 0
    
    async def initialize(self):
        """初始化缓存管理器"""
        try:
            redis_manager = await get_redis_manager()
            self.redis = redis_manager.client
            logger.info("缓存管理器初始化成功")
        except Exception as e:
            logger.error(f"缓存管理器初始化失败: {e}")
            raise
    
    def _generate_cache_key(self, prefix: str, *args, **kwargs) -> str:
        """
        生成缓存键
        
        Args:
            prefix: 键前缀
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            str: 缓存键
        """
        # 创建参数的哈希值
        params_str = json.dumps({
            "args": args,
            "kwargs": kwargs
        }, sort_keys=True, default=str)
        
        params_hash = hashlib.md5(params_str.encode()).hexdigest()
        
        return f"{settings.cache_prefix}:{prefix}:{params_hash}"
    
    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            Optional[Any]: 缓存值或None
        """
        try:
            if not self.redis:
                return None
            
            value = await self.redis.get(key)
            if value is None:
                self.miss_count += 1
                return None
            
            self.hit_count += 1
            return pickle.loads(value)
            
        except Exception as e:
            self.error_count += 1
            logger.warning(f"缓存获取失败: {e}", key=key)
            return None
    
    async def set(self, key: str, value: Any, ttl: int = 300) -> bool:
        """
        设置缓存值
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 过期时间（秒）
            
        Returns:
            bool: 是否成功
        """
        try:
            if not self.redis:
                return False
            
            serialized_value = pickle.dumps(value)
            await self.redis.setex(key, ttl, serialized_value)
            return True
            
        except Exception as e:
            self.error_count += 1
            logger.warning(f"缓存设置失败: {e}", key=key)
            return False
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 缓存键
            
        Returns:
            bool: 是否成功
        """
        try:
            if not self.redis:
                return False
            
            await self.redis.delete(key)
            return True
            
        except Exception as e:
            self.error_count += 1
            logger.warning(f"缓存删除失败: {e}", key=key)
            return False
    
    async def delete_pattern(self, pattern: str) -> int:
        """
        批量删除匹配模式的缓存
        
        Args:
            pattern: 匹配模式
            
        Returns:
            int: 删除的键数量
        """
        try:
            if not self.redis:
                return 0
            
            keys = await self.redis.keys(pattern)
            if keys:
                await self.redis.delete(*keys)
                return len(keys)
            return 0
            
        except Exception as e:
            self.error_count += 1
            logger.warning(f"批量缓存删除失败: {e}", pattern=pattern)
            return 0
    
    async def exists(self, key: str) -> bool:
        """
        检查缓存是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            bool: 是否存在
        """
        try:
            if not self.redis:
                return False
            
            return bool(await self.redis.exists(key))
            
        except Exception as e:
            self.error_count += 1
            logger.warning(f"缓存检查失败: {e}", key=key)
            return False
    
    async def get_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息
        
        Returns:
            Dict[str, Any]: 统计信息
        """
        total_requests = self.hit_count + self.miss_count
        hit_rate = (self.hit_count / total_requests * 100) if total_requests > 0 else 0
        
        return {
            "hit_count": self.hit_count,
            "miss_count": self.miss_count,
            "error_count": self.error_count,
            "total_requests": total_requests,
            "hit_rate": round(hit_rate, 2),
        }


# 全局缓存管理器实例
cache_manager = CacheManager()


def cache_result(
    ttl: int = 300,
    key_prefix: str = "default",
    skip_cache: Callable = None,
    user_aware: bool = True,
):
    """
    缓存函数结果的装饰器
    
    Args:
        ttl: 缓存过期时间（秒）
        key_prefix: 缓存键前缀
        skip_cache: 跳过缓存的条件函数
        user_aware: 是否包含用户ID在缓存键中
        
    Returns:
        装饰器函数
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            # 检查是否跳过缓存
            if skip_cache and skip_cache(*args, **kwargs):
                return await func(*args, **kwargs)
            
            # 构建缓存键
            cache_args = args
            cache_kwargs = kwargs.copy()
            
            # 如果启用用户感知，从参数中提取用户ID
            if user_aware:
                user_id = None
                if args and hasattr(args[0], 'id'):  # 第一个参数是用户对象
                    user_id = str(args[0].id)
                elif 'user_id' in kwargs:
                    user_id = str(kwargs['user_id'])
                elif 'current_user' in kwargs and hasattr(kwargs['current_user'], 'id'):
                    user_id = str(kwargs['current_user'].id)
                
                if user_id:
                    cache_kwargs['_user_id'] = user_id
            
            cache_key = cache_manager._generate_cache_key(
                f"{key_prefix}:{func.__name__}",
                *cache_args,
                **cache_kwargs
            )
            
            # 尝试从缓存获取
            cached_result = await cache_manager.get(cache_key)
            if cached_result is not None:
                logger.debug(f"缓存命中", key=cache_key)
                return cached_result
            
            # 执行函数并缓存结果
            try:
                result = await func(*args, **kwargs)
                await cache_manager.set(cache_key, result, ttl)
                logger.debug(f"缓存设置", key=cache_key, ttl=ttl)
                return result
                
            except Exception as e:
                logger.error(f"函数执行失败: {e}", func=func.__name__)
                raise
        
        # 添加缓存管理方法
        async def invalidate_cache(*args, **kwargs):
            """清除缓存"""
            cache_key = cache_manager._generate_cache_key(
                f"{key_prefix}:{func.__name__}",
                *args,
                **kwargs
            )
            await cache_manager.delete(cache_key)
        
        async def invalidate_all_cache():
            """清除所有相关缓存"""
            pattern = f"{settings.cache_prefix}:{key_prefix}:{func.__name__}:*"
            await cache_manager.delete_pattern(pattern)
        
        wrapper.invalidate_cache = invalidate_cache
        wrapper.invalidate_all_cache = invalidate_all_cache
        
        return wrapper
    return decorator


class UserCacheManager:
    """用户级别的缓存管理器"""
    
    def __init__(self, user_id: str):
        self.user_id = user_id
        self.prefix = f"user:{user_id}"
    
    async def set_user_data(self, key: str, data: Any, ttl: int = 3600):
        """设置用户数据缓存"""
        cache_key = f"{settings.cache_prefix}:{self.prefix}:{key}"
        await cache_manager.set(cache_key, data, ttl)
    
    async def get_user_data(self, key: str) -> Optional[Any]:
        """获取用户数据缓存"""
        cache_key = f"{settings.cache_prefix}:{self.prefix}:{key}"
        return await cache_manager.get(cache_key)
    
    async def delete_user_data(self, key: str):
        """删除用户数据缓存"""
        cache_key = f"{settings.cache_prefix}:{self.prefix}:{key}"
        await cache_manager.delete(cache_key)
    
    async def clear_user_cache(self):
        """清空用户所有缓存"""
        pattern = f"{settings.cache_prefix}:{self.prefix}:*"
        await cache_manager.delete_pattern(pattern)


class BatchCacheManager:
    """批量缓存管理器"""
    
    @staticmethod
    async def get_multi(keys: List[str]) -> Dict[str, Any]:
        """批量获取缓存"""
        if not cache_manager.redis:
            return {}
        
        try:
            pipe = cache_manager.redis.pipeline()
            for key in keys:
                pipe.get(key)
            
            values = await pipe.execute()
            result = {}
            
            for key, value in zip(keys, values):
                if value is not None:
                    try:
                        result[key] = pickle.loads(value)
                        cache_manager.hit_count += 1
                    except Exception as e:
                        logger.warning(f"缓存反序列化失败: {e}", key=key)
                        cache_manager.error_count += 1
                else:
                    cache_manager.miss_count += 1
            
            return result
            
        except Exception as e:
            cache_manager.error_count += len(keys)
            logger.error(f"批量缓存获取失败: {e}")
            return {}
    
    @staticmethod
    async def set_multi(data: Dict[str, Any], ttl: int = 300):
        """批量设置缓存"""
        if not cache_manager.redis:
            return
        
        try:
            pipe = cache_manager.redis.pipeline()
            for key, value in data.items():
                serialized_value = pickle.dumps(value)
                pipe.setex(key, ttl, serialized_value)
            
            await pipe.execute()
            
        except Exception as e:
            cache_manager.error_count += len(data)
            logger.error(f"批量缓存设置失败: {e}")


# 缓存装饰器的便捷别名
def cache_user_data(ttl: int = 3600):
    """用户数据缓存装饰器"""
    return cache_result(ttl=ttl, key_prefix="user_data", user_aware=True)


def cache_api_response(ttl: int = 300):
    """API响应缓存装饰器"""
    return cache_result(ttl=ttl, key_prefix="api_response", user_aware=True)


def cache_expensive_query(ttl: int = 1800):
    """昂贵查询缓存装饰器"""
    return cache_result(ttl=ttl, key_prefix="expensive_query", user_aware=True)


def cache_youtube_data(ttl: int = 3600):
    """YouTube数据缓存装饰器"""
    return cache_result(ttl=ttl, key_prefix="youtube_data", user_aware=True)


async def warm_up_cache():
    """缓存预热函数"""
    logger.info("开始缓存预热...")
    
    # 这里可以添加预热逻辑，比如：
    # - 预加载热门频道数据
    # - 预加载用户偏好设置
    # - 预加载搜索热词等
    
    logger.info("缓存预热完成")


async def cache_health_check() -> Dict[str, Any]:
    """缓存健康检查"""
    try:
        # 测试基本的读写操作
        test_key = f"{settings.cache_prefix}:health_check"
        test_value = {"timestamp": datetime.utcnow().isoformat()}
        
        # 写入测试
        await cache_manager.set(test_key, test_value, 10)
        
        # 读取测试
        retrieved_value = await cache_manager.get(test_key)
        
        # 清理测试数据
        await cache_manager.delete(test_key)
        
        # 获取统计信息
        stats = await cache_manager.get_stats()
        
        return {
            "healthy": retrieved_value is not None,
            "stats": stats,
            "test_successful": retrieved_value == test_value,
        }
        
    except Exception as e:
        logger.error(f"缓存健康检查失败: {e}")
        return {
            "healthy": False,
            "error": str(e),
            "stats": await cache_manager.get_stats(),
        }